<?php
class SzException extends Exception
{
    const EX_FILE_NAME = 'exception'; // exception config file name of the framework

    const EX_TYPE_FWK = '1'; // start number of framework exception
    const EX_TYPE_APP = '2'; // start number of application exception
    const EX_TYPE_MDU = '3'; // start number of module exception

    const EX_ERROR_CORD_EXPIRE_TIME = SzTime::MINUTE * 5;
    /**
     * @var SzAbstractCache
     */
    private $handleApiCache = null;

    /**
     * Construction, convert exception data from SzException format to Exception format.
     *
     * @param int $code
     * @param array $params default null, means no params given
     * @param string $moduleName module name, default null, required when exception was thrown in some module
     * @return SzException
     */
    public function __construct($code, $params = null, $moduleName = null)
    {
        parent::__construct('', $code);
        $this->message = $this->getExMsg($params, $moduleName);

        if ($code == 10502 || $code > 19999) {
            try {
                $identify = SzSystem::handleIdentify();
                if ($identify > 0) {
                    // 获取当前玩家 60 秒内，该报错数量，如果超过3次报错，则清空该玩家的缓存，以便让玩家上线。
                    $count = $this->handleApiCache($identify)->hGet($this->getApiCacheKey($identify), $code);
                    $count = ($count) ? $count : 0;
                    if ($count < 3) {
                        $count++;
                        $this->handleApiCache($identify)->hSet($this->getApiCacheKey($identify), $code, $count, self::EX_ERROR_CORD_EXPIRE_TIME);
                    } else {
                        $this->handleApiCache($identify)->hSet($this->getApiCacheKey($identify), $code, 0, self::EX_ERROR_CORD_EXPIRE_TIME);
                        SzUtility::deletePlayerCache($identify, 'userId');
                        SzLogger::get()->warn('Users often report errors and need to clear the cache. errorCode:' . $code);
                    }
                }
            } catch (Exception $e) {
                SzLogger::get()->error($e);
            }
        }
    }

    /**
     * Get formatted exception message.
     *
     * @param array $params default null
     * @param string $moduleName
     * @return string $message
     */
    public function getExMsg($params, $moduleName)
    {
        // determine exception type
        $code = (string)$this->getCode();
        $type = $code[0];

        // get message
        $message = '';
        switch ($type) {
            case self::EX_TYPE_FWK:
                $message = SzConfig::get()->loadFrameConfig(self::EX_FILE_NAME, $this->getCode());
                break;
            case self::EX_TYPE_APP:
                $message = SzConfig::get()->loadAppConfig(self::EX_FILE_NAME, $this->getCode());
                break;
            case self::EX_TYPE_MDU:
                $message = SzConfig::get()->loadModuleConfig($moduleName, self::EX_FILE_NAME, $this->getCode());
                break;
            default:
                $message = 'Error message type not defined!';
                break;
        }

        // input parameters
        if (!is_null($params)) {
            if (!is_array($params)) {
                $params = array($params);
            }
            $message = vsprintf($message, $params);
        }

        return $message;
    }

    private function getApiCacheKey($identify)
    {
        return 'ERROR_CODE:' . $identify;
    }

    private function handleApiCache($identify)
    {
        if ($this->handleApiCache == null) {
            $this->handleApiCache = SzContextFactory::get()->getAppCache($identify);
        }

        return $this->handleApiCache;
    }
}